#include "common.h"
#include <dirent.h>
#include <sys/stat.h>

extern void parse_config(char *);

char *config_file = NULL;

/* check maildir every check_interval seconds */
unsigned int check_interval;
/* show tray icon even when there is no new mail */
bool always_show_tray_icon;
/* launch emal_client when users click the tray icon */
char *email_client;
/* maildirs to be checked */
struct Maildir *maildirs;

GtkStatusIcon *tray_icon;
/* when new mails come, new_mail_icon will be showed in the trayer */
char *new_mail_icon;
/* when no new mails exist, if always_show_tray_icon is true, default_icon will
   be showed in the trayer */
char *default_icon;

/* tooltip */
#define MAX_TOOLTIP_LEN 1023
char tooltip[MAX_TOOLTIP_LEN+1];

/*
 * gtk destroy event handler.
 * @param widget is ignored
 * @param data is ignored
 */
static void
destroy_handler(GtkWidget *widget, gpointer data)
{
	struct Maildir *p;

        DEBUG_MSG("Maildir Checker exits.\n");

	if (email_client != NULL) free(email_client);
        if (new_mail_icon != NULL) free(new_mail_icon);
        if (default_icon != NULL) free(default_icon);

	p = maildirs;
	while (p != NULL) {
		free(p->path);
		if (p->descr != NULL)
			free(p->descr);
		p = maildirs->next_maildir;
		free(maildirs);
		maildirs = p;
	}

	gtk_main_quit();
}

/*
 * execute a mail client.
 */
static void 
exec_mail_client() {
	pid_t pid;
	if ((pid = fork()) == 0) {
		fprintf(stdout, "exec %s\n", email_client);
		system(email_client);
		exit(0);
	}
}

/*
 * popup menu
 * @param
 */
static void 
tray_icon_popup(GtkStatusIcon *status_icon, 
		guint button, 
		guint32 activate_time, 
		gpointer pop_up_menu)
{
	fprintf(stdout, "pop up menu.\n");

	gtk_menu_popup(GTK_MENU(pop_up_menu), 
		       NULL, 
		       NULL, 
		       gtk_status_icon_position_menu, 
		       status_icon, 
		       button, 
		       activate_time);
}

/*
 * check whether there are regular files in path maildir+"/new".
 * @param maildir path to a maildir
 * @return the number of regular files in path maildir+"/new"; or -1 for errors
 */
static int
check_maildir(char *maildir)
{
	char *path;
	int path_len;

	DIR *dp;
	struct dirent* dirp;
	struct stat st;
	int new_mail_num;

	fprintf(stdout, "check %s...", maildir);
	fflush(stdout);

	if (maildir == NULL) {
		ERR_MSG("empty maildir.");
		return -1;
	}
	
	path_len = strlen(maildir)+5 /* "/new\0" */;
	if (!(path = (char *)malloc(sizeof(char)*path_len))) {
		ERR_MSG("memory alloction fails.");
		return -1;
	}

	strncpy(path, maildir, sizeof(char)*(path_len-5));
	path[path_len-5] = '/';
	path[path_len-4] = 'n';
	path[path_len-3] = 'e';
	path[path_len-2] = 'w';
	path[path_len-1] = '\0';

	if (stat(path, &st) == -1) {
		fprintf(stderr, "ERR: %s\n", strerror(errno));
		free(path);
		return -1;
	}
	if (!S_ISDIR(st.st_mode)) {
		ERR_MSG("path is not a directory.");
		free(path);
		return -1;
	}
	
	if ((dp = opendir(path)) == NULL) {
		ERR_MSG("failed to open directory.");
		free(path);
		return -1;
	}
	
	errno = 0;
	new_mail_num = 0;
	while ((dirp = readdir(dp)) != NULL) {
		if (dirp->d_type == DT_REG)
			new_mail_num++;
	}
	if (errno != 0) {
		ERR_MSG("some errors");
		closedir(dp);
		free(path);
		return -1;
	}

	closedir(dp);
	free(path);

	fprintf(stdout, "%d\n", new_mail_num);

	return new_mail_num;
}

/*
 * check whether there are new mails.
 * @param data is ignored
 * @return always TRUE
 */
static gboolean
check_new_mail(gpointer data)
{
	// TODO: stop timer at the very beginning of this function
	//       and restart it at the end of this function

	int i, new_mail_num, remain_length;
	bool has_new_mail;
	char *strp;
	struct Maildir *cur_maildir;

	DEBUG_MSG("checking ...\n");

	strp = tooltip;
	remain_length = MAX_TOOLTIP_LEN;
	memset(tooltip, 0, (MAX_TOOLTIP_LEN+1)*sizeof(char));
	has_new_mail = FALSE;

	/* check. if there are new mails, has_new_mail will be set TRUE. */
	cur_maildir = maildirs;
        while (cur_maildir != NULL) {
		new_mail_num = 0;
		/* if (maildirs[i] == NULL) { */
		/* 	ERR_MSG("empty maildir"); */
		/* 	continue; */
		/* } */
		new_mail_num = check_maildir(cur_maildir->path);
		
		/* prepare for tooltip. we must pay attention to the buffer
		   overflow.*/
		if (remain_length <= 0) {
			/* if tooltip is already full, set the last four 
			   characters to " ..." */
			tooltip[MAX_TOOLTIP_LEN-4] = ' ';
			tooltip[MAX_TOOLTIP_LEN-2] = '.';
			tooltip[MAX_TOOLTIP_LEN-2] = '.';
			tooltip[MAX_TOOLTIP_LEN-1] = '.';
		} else if (new_mail_num > 0) {
			/* if there are still spaces in tooltip, append the new
			   string to it. */
			has_new_mail = TRUE;
			snprintf(strp, 
				 remain_length*sizeof(char),
				 "%d unread mail(s) in %s\n",
				 new_mail_num,
				 cur_maildir->descr);
			remain_length -= strlen(tooltip);
			strp += strlen(tooltip);
		}

		cur_maildir = cur_maildir->next_maildir;
	}

	/* show and hide tray icons */
	if (has_new_mail) {
                tooltip[strlen(tooltip)-1] = '\0';
                gtk_status_icon_set_from_file(tray_icon, new_mail_icon);
                gtk_status_icon_set_tooltip(tray_icon, tooltip);
                if (always_show_tray_icon == FALSE)
                        gtk_status_icon_set_visible(tray_icon, TRUE);
	} else {
                gtk_status_icon_set_from_file(tray_icon, default_icon);
                gtk_status_icon_set_tooltip(tray_icon, "No unread mail");
		if (always_show_tray_icon == FALSE)
                        gtk_status_icon_set_visible(tray_icon, FALSE);
	}

	return TRUE;
}

static void
init_config(int argc, char **argv)
{
	check_interval = 60;
	always_show_tray_icon = TRUE;
	email_client = NULL;
	maildirs = NULL;
	new_mail_icon = NULL;
	default_icon = NULL;

	if (getopt(argc, argv, "-c") == 'c')
		config_file = strdup(argv[optind]);
	else {
		char *home_path = getenv("HOME");

		if (home_path != NULL) {
			unsigned int config_path_len;

			g_strstrip(home_path);
			config_path_len = 
				strlen(home_path) +
				1 /* '/' */ +
				strlen(".maildir_checkrc") +
				1 /* '\0' */;
			config_file = (char *)
				malloc(sizeof(char)*config_path_len);
			sprintf(config_file, "%s/%s",
				home_path, ".maildir_checkrc");
		} else {
			config_file = strdup("/usr/etc/maildir_checkrc");
		}
	}
	if (config_file != NULL) {
		if (!access(config_file, R_OK))
			parse_config(config_file);
		free(config_file);
	}
}

int 
main(int argc, char **argv)
{
	GtkWidget *window;
	GtkWidget *menu, *menu_item_view, *menu_item_check, *menu_item_exit;
	guint timer;

	gtk_init(&argc, &argv);

	init_config(argc, argv);

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(window), "Maildir Checker");
	g_signal_connect(G_OBJECT(window), "destroy", 
			 G_CALLBACK(destroy_handler), NULL);

	timer = g_timeout_add(check_interval*1000, check_new_mail, NULL);

        tray_icon = gtk_status_icon_new_from_file(default_icon);

	menu = gtk_menu_new();
	menu_item_check = gtk_menu_item_new_with_label("Check Now");
	menu_item_view = gtk_menu_item_new_with_label("Read Mails");
	menu_item_exit = gtk_menu_item_new_with_label("Exit");
	g_signal_connect(G_OBJECT(menu_item_check), 
			 "activate", 
			 G_CALLBACK(check_new_mail),
			 NULL);
	g_signal_connect(G_OBJECT(menu_item_view),
			 "activate",
			 G_CALLBACK(exec_mail_client),
			 NULL);
	g_signal_connect(G_OBJECT(menu_item_exit),
			 "activate",
			 G_CALLBACK(destroy_handler),
			 NULL);
	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item_check);
	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item_view);
	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item_exit);
	gtk_widget_show_all(menu);
	g_signal_connect(GTK_STATUS_ICON(tray_icon),
			 "popup-menu",
			 GTK_SIGNAL_FUNC(tray_icon_popup),
			 menu);

	check_new_mail(NULL);

	gtk_main();

	return 0;
}
